//
//  WBEngine.m
//  SinaWeiBoSDK
//  Based on OAuth 2.0
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//  OTHER DEALINGS IN THE SOFTWARE.
//
//  Copyright 2011 Sina. All rights reserved.
//

#import "WBEngine.h"
#import "SFHFKeychainUtils.h"
#import "WBSDKGlobal.h"
#import "WBUtil.h"
#import "header.h"

#define kWBURLSchemePrefix              @"WB_"

#define kWBKeychainServiceNameSuffix    @"_WeiBoServiceName"
#define kWBKeychainUserID               @"WeiBoUserID"
#define kWBKeychainAccessToken          @"WeiBoAccessToken"
#define kWBKeychainExpireTime           @"WeiBoExpireTime"

@interface WBEngine (Private)

- (NSString *)urlSchemeString;



@end

@implementation WBEngine

@synthesize appKey;
@synthesize appSecret;
@synthesize userID;
@synthesize accessToken;
@synthesize expireTime;
@synthesize redirectURI;
@synthesize isUserExclusive;
@synthesize request;
@synthesize authorize;
@synthesize delegate;
@synthesize rootViewController;

#pragma mark - WBEngine Life Circle

- (id)initWithAppKey:(NSString *)theAppKey appSecret:(NSString *)theAppSecret
{
    if (self = [super init])
    {
        self.appKey = theAppKey;
        self.appSecret = theAppSecret;
        
        isUserExclusive = NO;
        _hasError = NO;
        _isAuthError = NO;
        
        [self readAuthorizeDataFromKeychain];
    }
    
    return self;
}

- (void)dealloc
{
    [appKey release], appKey = nil;
    [appSecret release], appSecret = nil;
    
    [userID release], userID = nil;
    [accessToken release], accessToken = nil;
    
    [redirectURI release], redirectURI = nil;
    
    [request setDelegate:nil];
    [request disconnect];
    [request release], request = nil;
    
    [authorize setDelegate:nil];
    [authorize release], authorize = nil;
    
    delegate = nil;
    rootViewController = nil;
    
    [_errorDetail release]; _errorDetail = nil;
    [_errorMessage release]; _errorMessage = nil;    
    [super dealloc];
}

#pragma mark - WBEngine Private Methods

- (NSString *)urlSchemeString
{
    return [NSString stringWithFormat:@"%@%@", kWBURLSchemePrefix, appKey];
}

- (void)saveAuthorizeDataToKeychain
{
    NSString *serviceName = [[self urlSchemeString] stringByAppendingString:kWBKeychainServiceNameSuffix];
    [SFHFKeychainUtils storeUsername:kWBKeychainUserID andPassword:userID forServiceName:serviceName updateExisting:YES error:nil];
	[SFHFKeychainUtils storeUsername:kWBKeychainAccessToken andPassword:accessToken forServiceName:serviceName updateExisting:YES error:nil];
	[SFHFKeychainUtils storeUsername:kWBKeychainExpireTime andPassword:[NSString stringWithFormat:@"%lf", expireTime] forServiceName:serviceName updateExisting:YES error:nil];
}

- (void)readAuthorizeDataFromKeychain
{
    NSString *serviceName = [[self urlSchemeString] stringByAppendingString:kWBKeychainServiceNameSuffix];
    self.userID = [SFHFKeychainUtils getPasswordForUsername:kWBKeychainUserID andServiceName:serviceName error:nil];
    self.accessToken = [SFHFKeychainUtils getPasswordForUsername:kWBKeychainAccessToken andServiceName:serviceName error:nil];
    self.expireTime = [[SFHFKeychainUtils getPasswordForUsername:kWBKeychainExpireTime andServiceName:serviceName error:nil] doubleValue];
}

- (void)deleteAuthorizeDataInKeychain
{
    self.userID = nil;
    self.accessToken = nil;
    self.expireTime = 0;
    
    NSString *serviceName = [[self urlSchemeString] stringByAppendingString:kWBKeychainServiceNameSuffix];
    [SFHFKeychainUtils deleteItemForUsername:kWBKeychainUserID andServiceName:serviceName error:nil];
	[SFHFKeychainUtils deleteItemForUsername:kWBKeychainAccessToken andServiceName:serviceName error:nil];
	[SFHFKeychainUtils deleteItemForUsername:kWBKeychainExpireTime andServiceName:serviceName error:nil];
}

#pragma mark - WBEngine Public Methods

#pragma mark Authorization

- (void)logIn:(UIWebView*)webview
{
    if ([self isLoggedIn])
    {
        if ([delegate respondsToSelector:@selector(engineAlreadyLoggedIn:)])
        {
            [delegate engineAlreadyLoggedIn:self];
        }
        if (isUserExclusive)
        {
            return;
        }
    }
    
    WBAuthorize *auth = [[WBAuthorize alloc] initWithAppKey:appKey appSecret:appSecret];
    [auth setRootViewController:rootViewController];
    [auth setDelegate:self];
    self.authorize = auth;
    [auth release];
    
    if ([redirectURI length] > 0)
    {
        [authorize setRedirectURI:redirectURI];
    }
    else
    {
        [authorize setRedirectURI:@"http://"];
    }
    
    [authorize startAuthorize:webview];
}

- (void)logInUsingUserID:(NSString *)theUserID password:(NSString *)thePassword
{
    self.userID = theUserID;
    
    if ([self isLoggedIn])
    {
        if ([delegate respondsToSelector:@selector(engineAlreadyLoggedIn:)])
        {
            [delegate engineAlreadyLoggedIn:self];
        }
        if (isUserExclusive)
        {
            return;
        }
    }
    
    WBAuthorize *auth = [[WBAuthorize alloc] initWithAppKey:appKey appSecret:appSecret];
    [auth setRootViewController:rootViewController];
    [auth setDelegate:self];
    self.authorize = auth;
    [auth release];
    
    if ([redirectURI length] > 0)
    {
        [authorize setRedirectURI:redirectURI];
    }
    else
    {
        [authorize setRedirectURI:@"http://"];
    }
    
    [authorize startAuthorizeUsingUserID:theUserID password:thePassword];
}

- (void)logOut
{
    [self deleteAuthorizeDataInKeychain];
    
    if ([delegate respondsToSelector:@selector(engineDidLogOut:)])
    {
        [delegate engineDidLogOut:self];
    }
}

- (BOOL)isLoggedIn
{
    //    return userID && accessToken && refreshToken;
    return userID && accessToken && (expireTime > 0);
}

- (BOOL)isAuthorizeExpired
{
    if ([[NSDate date] timeIntervalSince1970] > expireTime)
    {
        // force to log out
        [self deleteAuthorizeDataInKeychain];
        return YES;
    }
    return NO;
}

#pragma mark Request

- (void)loadRequestWithMethodName:(NSString *)methodName
                       httpMethod:(NSString *)httpMethod
                           params:(NSDictionary *)params
                     postDataType:(WBRequestPostDataType)postDataType
                 httpHeaderFields:(NSDictionary *)httpHeaderFields
{
    [self loadRequestWithMethodName:methodName 
                         httpMethod:httpMethod 
                             params:params 
                       postDataType:postDataType 
                   httpHeaderFields:httpHeaderFields 
                        doCheckAuth:YES];
}

- (void)loadRequestWithMethodName:(NSString *)methodName
                       httpMethod:(NSString *)httpMethod
                           params:(NSDictionary *)params
                     postDataType:(WBRequestPostDataType)postDataType
                 httpHeaderFields:(NSDictionary *)httpHeaderFields
                      doCheckAuth:(BOOL)check
{
    // Step 1.
    // Check if the user has been logged in.
	if (check && ![self isLoggedIn])
	{
        if ([delegate respondsToSelector:@selector(engineNotAuthorized:)])
        {
            [delegate engineNotAuthorized:self];
        }
        return;
	}
    
	// Step 2.
    // Check if the access token is expired.
    if (check && [self isAuthorizeExpired])
    {
        if ([delegate respondsToSelector:@selector(engineAuthorizeExpired:)])
        {
            [delegate engineAuthorizeExpired:self];
        }
        return;
    }
    
    [request disconnect];
    
    if (check)
        self.request = [WBRequest requestWithAccessToken:accessToken
                                                     url:[NSString stringWithFormat:@"%@%@", kWBSDKAPIDomain, methodName]
                                              httpMethod:httpMethod
                                                  params:params
                                            postDataType:postDataType
                                        httpHeaderFields:httpHeaderFields
                                                delegate:self];
    else {
        self.request = [WBRequest requestWithSource:self.appKey
                                                     url:[NSString stringWithFormat:@"%@%@", kWBSDKAPIDomain, methodName]
                                              httpMethod:httpMethod
                                                  params:params
                                            postDataType:postDataType
                                        httpHeaderFields:httpHeaderFields
                                                delegate:self];
    }
	
	[request connect];
}




-(void)requestAccessTokenWithAuthorizeCode:(NSString *)code {
    [authorize requestAccessTokenWithAuthorizeCode:code];
}
-(void) api_getUsersShow:(long long)uid {    
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:[NSString stringWithFormat:@"%lld", uid] forKey:@"uid"];
    [self loadRequestWithMethodName:@"users/show.json"
                           httpMethod:@"GET"
                               params:params
                         postDataType:kWBRequestPostDataTypeNone
                     httpHeaderFields:nil];
}
-(void) api_getStatusesShow:(long long)uid {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:[NSString stringWithFormat:@"%lld", uid] forKey:@"id"];
    [self loadRequestWithMethodName:@"statuses/show.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil];
}
-(void) api_getFriendshipsFriendsBilateral:(long long)uid count:(int)count page:(int)page {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:[NSString stringWithFormat:@"%lld", uid] forKey:@"uid"];
    [params setObject:[NSString stringWithFormat:@"%d", count] forKey:@"count"];
    [params setObject:[NSString stringWithFormat:@"%d", page] forKey:@"page"];
    [params setObject:[NSString stringWithFormat:@"%d", 0] forKey:@"sort"];
    [self loadRequestWithMethodName:@"friendships/friends/bilateral.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil];
}
-(void) api_getFriendshipsShow_source_id:(long long)source_id target_id:(long long)target_id {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:[NSString stringWithFormat:@"%lld", source_id] forKey:@"source_id"];
    [params setObject:[NSString stringWithFormat:@"%lld", target_id] forKey:@"target_id"];
    [self loadRequestWithMethodName:@"friendships/show.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil];
}
-(void) api_postFriendshipsCreate:(long long)uid {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:[NSString stringWithFormat:@"%lld", uid] forKey:@"uid"];
    [self loadRequestWithMethodName:@"friendships/create.json"
                         httpMethod:@"POST"
                             params:params
                       postDataType:kWBRequestPostDataTypeNormal
                   httpHeaderFields:nil];
}

-(void) api_getShortUrlShorten:(NSString*)url_long {
    DebugLog(@"api_getShortUrlShorten -- url_long %@", url_long);
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_long forKey:@"url_long"];
    [self loadRequestWithMethodName:@"short_url/shorten.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil
                        doCheckAuth:FALSE];
}
-(void) api_getShortUrlClicks:(NSString*)url_short {
    DebugLog(@"api_getShortUrlClicks -- url_short %@", url_short);
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [self loadRequestWithMethodName:@"short_url/clicks.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil
                        doCheckAuth:FALSE];
}
-(void) api_getShortUrlExpand:(NSString*)url_short {
    DebugLog(@"api_getShortUrlExpand -- url_short %@", url_short);
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [self loadRequestWithMethodName:@"short_url/expand.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil
                        doCheckAuth:FALSE];
}
-(void) api_getShortUrlShareCounts:(NSString*)url_short {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [self loadRequestWithMethodName:@"short_url/share/counts.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil
                        doCheckAuth:FALSE];
}
-(void) api_getShortUrlCommentCounts:(NSString*)url_short {
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [self loadRequestWithMethodName:@"short_url/comment/counts.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil
                        doCheckAuth:FALSE];
}
-(void) api_getShortUrlShareStatuses:(NSString*)url_short max_id:(long long)max_id count:(int)count{//获取包含指定单个短链接的最新微博内容 
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [params setObject:[NSString stringWithFormat:@"%lld", max_id] forKey:@"max_id"];
    [params setObject:[NSString stringWithFormat:@"%d", count] forKey:@"count"];
    [self loadRequestWithMethodName:@"short_url/share/statuses.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil];
}
-(void) api_getShortUrlCommentComments:(NSString*)url_short max_id:(long long)max_id count:(int)count{//获取包含指定单个短链接的最新微博评论 
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];
    [params setObject:url_short forKey:@"url_short"];
    [params setObject:[NSString stringWithFormat:@"%lld", max_id] forKey:@"max_id"];
    [params setObject:[NSString stringWithFormat:@"%d", count] forKey:@"count"];
    [self loadRequestWithMethodName:@"short_url/comment/comments.json"
                         httpMethod:@"GET"
                             params:params
                       postDataType:kWBRequestPostDataTypeNone
                   httpHeaderFields:nil];
}




- (void)sendWeiBoWithText:(NSString *)text image:(UIImage *)image
{
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:2];

    //NSString *sendText = [text URLEncodedString];
    
	[params setObject:(text ? text : @"") forKey:@"status"];
	
    if (image)
    {
		[params setObject:image forKey:@"pic"];

        [self loadRequestWithMethodName:@"statuses/upload.json"
                             httpMethod:@"POST"
                                 params:params
                           postDataType:kWBRequestPostDataTypeMultipart
                       httpHeaderFields:nil];
    }
    else
    {
        [self loadRequestWithMethodName:@"statuses/update.json"
                             httpMethod:@"POST"
                                 params:params
                           postDataType:kWBRequestPostDataTypeNormal
                       httpHeaderFields:nil];
    }
}

#pragma mark - WBAuthorizeDelegate Methods

- (void)authorize:(WBAuthorize *)authorize didSucceedWithAccessToken:(NSString *)theAccessToken userID:(NSString *)theUserID expiresIn:(NSInteger)seconds
{
    self.accessToken = theAccessToken;
    self.userID = theUserID;
    self.expireTime = [[NSDate date] timeIntervalSince1970] + seconds;
    
    [self saveAuthorizeDataToKeychain];
    
    if ([delegate respondsToSelector:@selector(engineDidLogIn:)])
    {
        [delegate engineDidLogIn:self];
    }
}

- (void)authorize:(WBAuthorize *)authorize didFailWithError:(NSError *)error
{
    if ([delegate respondsToSelector:@selector(engine:didFailToLogInWithError:)])
    {
        [delegate engine:self didFailToLogInWithError:error];
    }
}

#pragma mark - WBRequestDelegate Methods

- (void)request:(WBRequest *)request didFinishLoadingWithResult:(id)result
{
    _hasError = NO;
    _isAuthError = NO;
    if ([delegate respondsToSelector:@selector(engine:requestDidSucceedWithResult:)])
    {
        [delegate engine:self requestDidSucceedWithResult:result];
    }
}

- (void)request:(WBRequest *)request didFailWithError:(NSError *)error
{
    _hasError = YES; // TODO:
    _isAuthError = NO;
    
    [_errorMessage release]; _errorMessage = nil;
    [_errorDetail release]; _errorDetail = nil;
    
    if (error && error.userInfo) {
        int error_code = [[error.userInfo valueForKey:@"error_code"] intValue];
        NSString* _error = [error.userInfo valueForKey:@"error"];
        // 因为授权后，在服务端可以取消 授权，，，，授权很可能会 已经失效。
        if (error_code == 21332 && [@"invalid_access_token" compare:_error] == 0) {
            _isAuthError = TRUE;
            //[self logOut]; 告诉外部，让外部 log out。。。
        }
        
        _errorMessage = [_error copy];
        _errorDetail = [[NSString stringWithFormat:@"%@", error] copy];
    }
    if ([delegate respondsToSelector:@selector(engine:requestDidFailWithError:)])
    {
        [delegate engine:self requestDidFailWithError:error];
    }
}


#pragma mark - 
-(BOOL) isAuthError { return _isAuthError; }
-(BOOL) hasError { return _hasError; }
-(NSString*) errorMessage {
    return [[_errorMessage copy] autorelease];
}
-(NSString*) errorDetail {
    return [[_errorDetail copy] autorelease];
}
@end

